#include "common/include/data.h"
#include "klibc/include/stdio.h"
#include "klibc/include/assert.h"
#include "klibc/include/sys.h"
#include "driver/include/keyboard.h"
#include "driver/include/console.h"


#define FLAG_BREAK      0x0080          /* Break Code                   */
#define FLAG_EXT        0x0100          /* Normal function keys         */
#define FLAG_SHIFT_L    0x0200          /* Shift key                    */
#define FLAG_SHIFT_R    0x0400          /* Shift key                    */
#define FLAG_CTRL_L     0x0800          /* Control key                  */
#define FLAG_CTRL_R     0x1000          /* Control key                  */
#define FLAG_ALT_L      0x2000          /* Alternate key                */
#define FLAG_ALT_R      0x4000          /* Alternate key                */
#define FLAG_PAD        0x8000          /* keys in num pad              */

#define MASK_RAW        0x01FF          /* raw key value = code passed to tty & MASK_RAW
the value can be found either in the keymap column 0
or in the list below */

/* Special keys */
#define ESC             (0x01 + FLAG_EXT)       /* Esc          */
#define TAB             (0x02 + FLAG_EXT)       /* Tab          */
#define ENTER           (0x03 + FLAG_EXT)       /* Enter        */
#define BACKSPACE       (0x04 + FLAG_EXT)       /* BackSpace    */

#define GUI_L           (0x05 + FLAG_EXT)       /* L GUI        */
#define GUI_R           (0x06 + FLAG_EXT)       /* R GUI        */
#define APPS            (0x07 + FLAG_EXT)       /* APPS */

/* Shift, Ctrl, Alt */
#define SHIFT_L         (0x08 + FLAG_EXT)       /* L Shift      */
#define SHIFT_R         (0x09 + FLAG_EXT)       /* R Shift      */
#define CTRL_L          (0x0A + FLAG_EXT)       /* L Ctrl       */
#define CTRL_R          (0x0B + FLAG_EXT)       /* R Ctrl       */
#define ALT_L           (0x0C + FLAG_EXT)       /* L Alt        */
#define ALT_R           (0x0D + FLAG_EXT)       /* R Alt        */

/* Lock keys */
#define CAPS_LOCK       (0x0E + FLAG_EXT)       /* Caps Lock    */
#define NUM_LOCK        (0x0F + FLAG_EXT)       /* Number Lock  */
#define SCROLL_LOCK     (0x10 + FLAG_EXT)       /* Scroll Lock  */

/* Function keys */
#define F1              (0x11 + FLAG_EXT)       /* F1           */
#define F2              (0x12 + FLAG_EXT)       /* F2           */
#define F3              (0x13 + FLAG_EXT)       /* F3           */
#define F4              (0x14 + FLAG_EXT)       /* F4           */
#define F5              (0x15 + FLAG_EXT)       /* F5           */
#define F6              (0x16 + FLAG_EXT)       /* F6           */
#define F7              (0x17 + FLAG_EXT)       /* F7           */
#define F8              (0x18 + FLAG_EXT)       /* F8           */
#define F9              (0x19 + FLAG_EXT)       /* F9           */
#define F10             (0x1A + FLAG_EXT)       /* F10          */
#define F11             (0x1B + FLAG_EXT)       /* F11          */
#define F12             (0x1C + FLAG_EXT)       /* F12          */

/* Control Pad */
#define PRINTSCREEN     (0x1D + FLAG_EXT)       /* Print Screen */
#define PAUSEBREAK      (0x1E + FLAG_EXT)       /* Pause/Break  */
#define INSERT          (0x1F + FLAG_EXT)       /* Insert       */
#define DELETE          (0x20 + FLAG_EXT)       /* Delete       */
#define HOME            (0x21 + FLAG_EXT)       /* Home         */
#define END             (0x22 + FLAG_EXT)       /* End          */
#define PAGEUP          (0x23 + FLAG_EXT)       /* Page Up      */
#define PAGEDOWN        (0x24 + FLAG_EXT)       /* Page Down    */
#define UP              (0x25 + FLAG_EXT)       /* Up           */
#define DOWN            (0x26 + FLAG_EXT)       /* Down         */
#define LEFT            (0x27 + FLAG_EXT)       /* Left         */
#define RIGHT           (0x28 + FLAG_EXT)       /* Right        */

/* ACPI keys */
#define POWER           (0x29 + FLAG_EXT)       /* Power        */
#define SLEEP           (0x2A + FLAG_EXT)       /* Sleep        */
#define WAKE            (0x2B + FLAG_EXT)       /* Wake Up      */

/* Num Pad */
#define PAD_SLASH       (0x2C + FLAG_EXT)       /* /            */
#define PAD_STAR        (0x2D + FLAG_EXT)       /* *            */
#define PAD_MINUS       (0x2E + FLAG_EXT)       /* -            */
#define PAD_PLUS        (0x2F + FLAG_EXT)       /* +            */
#define PAD_ENTER       (0x30 + FLAG_EXT)       /* Enter        */
#define PAD_DOT         (0x31 + FLAG_EXT)       /* .            */
#define PAD_0           (0x32 + FLAG_EXT)       /* 0            */
#define PAD_1           (0x33 + FLAG_EXT)       /* 1            */
#define PAD_2           (0x34 + FLAG_EXT)       /* 2            */
#define PAD_3           (0x35 + FLAG_EXT)       /* 3            */
#define PAD_4           (0x36 + FLAG_EXT)       /* 4            */
#define PAD_5           (0x37 + FLAG_EXT)       /* 5            */
#define PAD_6           (0x38 + FLAG_EXT)       /* 6            */
#define PAD_7           (0x39 + FLAG_EXT)       /* 7            */
#define PAD_8           (0x3A + FLAG_EXT)       /* 8            */
#define PAD_9           (0x3B + FLAG_EXT)       /* 9            */
#define PAD_UP          PAD_8                   /* Up           */
#define PAD_DOWN        PAD_2                   /* Down         */
#define PAD_LEFT        PAD_4                   /* Left         */
#define PAD_RIGHT       PAD_6                   /* Right        */
#define PAD_HOME        PAD_7                   /* Home         */
#define PAD_END         PAD_1                   /* End          */
#define PAD_PAGEUP      PAD_9                   /* Page Up      */
#define PAD_PAGEDOWN    PAD_3                   /* Page Down    */
#define PAD_INS         PAD_0                   /* Ins          */
#define PAD_MID         PAD_5                   /* Middle key   */
#define PAD_DEL         PAD_DOT                 /* Del          */


static unsigned int keymap[3 * 0x80] = {
    /* scan-code                    !Shift          Shift           E0 XX   */
    /* ==================================================================== */
    /* 0x00 - none          */      0,              0,              0,
    /* 0x01 - ESC           */      ESC,            ESC,            0,
    /* 0x02 - '1'           */      '1',            '!',            0,
    /* 0x03 - '2'           */      '2',            '@',            0,
    /* 0x04 - '3'           */      '3',            '#',            0,
    /* 0x05 - '4'           */      '4',            '$',            0,
    /* 0x06 - '5'           */      '5',            '%',            0,
    /* 0x07 - '6'           */      '6',            '^',            0,
    /* 0x08 - '7'           */      '7',            '&',            0,
    /* 0x09 - '8'           */      '8',            '*',            0,
    /* 0x0A - '9'           */      '9',            '(',            0,
    /* 0x0B - '0'           */      '0',            ')',            0,
    /* 0x0C - '-'           */      '-',            '_',            0,
    /* 0x0D - '='           */      '=',            '+',            0,
    /* 0x0E - BS            */      BACKSPACE,      BACKSPACE,      0,
    /* 0x0F - TAB           */      TAB,            TAB,            0,
    /* 0x10 - 'q'           */      'q',            'Q',            0,
    /* 0x11 - 'w'           */      'w',            'W',            0,
    /* 0x12 - 'e'           */      'e',            'E',            0,
    /* 0x13 - 'r'           */      'r',            'R',            0,
    /* 0x14 - 't'           */      't',            'T',            0,
    /* 0x15 - 'y'           */      'y',            'Y',            0,
    /* 0x16 - 'u'           */      'u',            'U',            0,
    /* 0x17 - 'i'           */      'i',            'I',            0,
    /* 0x18 - 'o'           */      'o',            'O',            0,
    /* 0x19 - 'p'           */      'p',            'P',            0,
    /* 0x1A - '['           */      '[',            '{',            0,
    /* 0x1B - ']'           */      ']',            '}',            0,
    /* 0x1C - CR/LF         */      ENTER,          ENTER,          PAD_ENTER,
    /* 0x1D - l. Ctrl       */      CTRL_L,         CTRL_L,         CTRL_R,
    /* 0x1E - 'a'           */      'a',            'A',            0,
    /* 0x1F - 's'           */      's',            'S',            0,
    /* 0x20 - 'd'           */      'd',            'D',            0,
    /* 0x21 - 'f'           */      'f',            'F',            0,
    /* 0x22 - 'g'           */      'g',            'G',            0,
    /* 0x23 - 'h'           */      'h',            'H',            0,
    /* 0x24 - 'j'           */      'j',            'J',            0,
    /* 0x25 - 'k'           */      'k',            'K',            0,
    /* 0x26 - 'l'           */      'l',            'L',            0,
    /* 0x27 - ';'           */      ';',            ':',            0,
    /* 0x28 - '\''          */      '\'',           '"',            0,
    /* 0x29 - '`'           */      '`',            '~',            0,
    /* 0x2A - l. SHIFT      */      SHIFT_L,        SHIFT_L,        0,
    /* 0x2B - '\'           */      '\\',           '|',            0,
    /* 0x2C - 'z'           */      'z',            'Z',            0,
    /* 0x2D - 'x'           */      'x',            'X',            0,
    /* 0x2E - 'c'           */      'c',            'C',            0,
    /* 0x2F - 'v'           */      'v',            'V',            0,
    /* 0x30 - 'b'           */      'b',            'B',            0,
    /* 0x31 - 'n'           */      'n',            'N',            0,
    /* 0x32 - 'm'           */      'm',            'M',            0,
    /* 0x33 - ','           */      ',',            '<',            0,
    /* 0x34 - '.'           */      '.',            '>',            0,
    /* 0x35 - '/'           */      '/',            '?',            PAD_SLASH,
    /* 0x36 - r. SHIFT      */      SHIFT_R,        SHIFT_R,        0,
    /* 0x37 - '*'           */      '*',            '*',            0,
    /* 0x38 - ALT           */      ALT_L,          ALT_L,          ALT_R,
    /* 0x39 - ' '           */      ' ',            ' ',            0,
    /* 0x3A - CapsLock      */      CAPS_LOCK,      CAPS_LOCK,      0,
    /* 0x3B - F1            */      F1,             F1,             0,
    /* 0x3C - F2            */      F2,             F2,             0,
    /* 0x3D - F3            */      F3,             F3,             0,
    /* 0x3E - F4            */      F4,             F4,             0,
    /* 0x3F - F5            */      F5,             F5,             0,
    /* 0x40 - F6            */      F6,             F6,             0,
    /* 0x41 - F7            */      F7,             F7,             0,
    /* 0x42 - F8            */      F8,             F8,             0,
    /* 0x43 - F9            */      F9,             F9,             0,
    /* 0x44 - F10           */      F10,            F10,            0,
    /* 0x45 - NumLock       */      NUM_LOCK,       NUM_LOCK,       0,
    /* 0x46 - ScrLock       */      SCROLL_LOCK,    SCROLL_LOCK,    0,
    /* 0x47 - Home          */      PAD_HOME,       '7',            HOME,
    /* 0x48 - CurUp         */      PAD_UP,         '8',            UP,
    /* 0x49 - PgUp          */      PAD_PAGEUP,     '9',            PAGEUP,
    /* 0x4A - '-'           */      PAD_MINUS,      '-',            0,
    /* 0x4B - Left          */      PAD_LEFT,       '4',            LEFT,
    /* 0x4C - MID           */      PAD_MID,        '5',            0,
    /* 0x4D - Right         */      PAD_RIGHT,      '6',            RIGHT,
    /* 0x4E - '+'           */      PAD_PLUS,       '+',            0,
    /* 0x4F - End           */      PAD_END,        '1',            END,
    /* 0x50 - Down          */      PAD_DOWN,       '2',            DOWN,
    /* 0x51 - PgDown        */      PAD_PAGEDOWN,   '3',            PAGEDOWN,
    /* 0x52 - Insert        */      PAD_INS,        '0',            INSERT,
    /* 0x53 - Delete        */      PAD_DOT,        '.',            DELETE,
    /* 0x54 - Enter         */      0,              0,              0,
    /* 0x55 - ???           */      0,              0,              0,
    /* 0x56 - ???           */      0,              0,              0,
    /* 0x57 - F11           */      F11,            F11,            0,
    /* 0x58 - F12           */      F12,            F12,            0,
    /* 0x59 - ???           */      0,              0,              0,
    /* 0x5A - ???           */      0,              0,              0,
    /* 0x5B - ???           */      0,              0,              GUI_L,
    /* 0x5C - ???           */      0,              0,              GUI_R,
    /* 0x5D - ???           */      0,              0,              APPS,
    /* 0x5E - ???           */      0,              0,              0,
    /* 0x5F - ???           */      0,              0,              0,
    /* 0x60 - ???           */      0,              0,              0,
    /* 0x61 - ???           */      0,              0,              0,
    /* 0x62 - ???           */      0,              0,              0,
    /* 0x63 - ???           */      0,              0,              0,
    /* 0x64 - ???           */      0,              0,              0,
    /* 0x65 - ???           */      0,              0,              0,
    /* 0x66 - ???           */      0,              0,              0,
    /* 0x67 - ???           */      0,              0,              0,
    /* 0x68 - ???           */      0,              0,              0,
    /* 0x69 - ???           */      0,              0,              0,
    /* 0x6A - ???           */      0,              0,              0,
    /* 0x6B - ???           */      0,              0,              0,
    /* 0x6C - ???           */      0,              0,              0,
    /* 0x6D - ???           */      0,              0,              0,
    /* 0x6E - ???           */      0,              0,              0,
    /* 0x6F - ???           */      0,              0,              0,
    /* 0x70 - ???           */      0,              0,              0,
    /* 0x71 - ???           */      0,              0,              0,
    /* 0x72 - ???           */      0,              0,              0,
    /* 0x73 - ???           */      0,              0,              0,
    /* 0x74 - ???           */      0,              0,              0,
    /* 0x75 - ???           */      0,              0,              0,
    /* 0x76 - ???           */      0,              0,              0,
    /* 0x77 - ???           */      0,              0,              0,
    /* 0x78 - ???           */      0,              0,              0,
    /* 0x78 - ???           */      0,              0,              0,
    /* 0x7A - ???           */      0,              0,              0,
    /* 0x7B - ???           */      0,              0,              0,
    /* 0x7C - ???           */      0,              0,              0,
    /* 0x7D - ???           */      0,              0,              0,
    /* 0x7E - ???           */      0,              0,              0,
    /* 0x7F - ???           */      0,              0,              0
};


/*
 * Keyboard states
 */
static int caps_lock = 0;
static int num_lock = 0;
static int scroll_lock = 0;

static int shift_hold = 0;
static int ctrl_hold = 0;
static int alt_hold = 0;
static int cmd_hold = 0;
static int fn_hold = 0;


static void convert_scan_code(unsigned int scan_code, unsigned int *key, int *release)
{
    if (scan_code >> 7) {
        *release = 1;
    } else {
        *release = 0;
    }
    
    unsigned int real_scan = (scan_code & 0xFFFFFF7F) * 3;
    if (shift_hold & !caps_lock) {
        real_scan++;
    }
    
    *key = keymap[real_scan];
}

static char process_key(unsigned int scan_code)
{
    unsigned int key = 0;
    int release = 0;
    char printable = '\0';
    
    convert_scan_code(scan_code, &key, &release);
    
    switch (key) {
    case ENTER:
        printable = release ? printable : '\n';
        break;
    case TAB:
        printable = release ? printable : '\t';
        break;
    case BACKSPACE:
        printable = release ? printable : '\b';
        break;
    
    case CAPS_LOCK:
        caps_lock = release ? caps_lock : !caps_lock;
        break;
    
    case SHIFT_L:
    case SHIFT_R:
        shift_hold = release ? 0 : 1;
        break;
    case CTRL_L:
    case CTRL_R:
        ctrl_hold = release ? 0 : 1;
        break;
    case ALT_L:
    case ALT_R:
        alt_hold = release ? 0 : 1;
        break;
    
    default:
        if (!release) {
            if (caps_lock && !shift_hold) {
                key += 'A' - 'a';
            }
            printable = (char)key;
        }
        break;
    }
    
    return printable;
}


static asmlinkage void keyboard_interrupt_handler(msg_t *msg)
{
    u8 buf[sizeof(ulong)];
    int buf_size = (int)msg->params[2].value;
    ulong data = msg->params[3].value;
    int trans = (int)msg->params[4].value;
    int cur = buf_size;
    
    char print_buf[sizeof(ulong) + 1];
    int print_count = 0;
    int i;
    
    assert(buf_size <= sizeof(buf));
    
//     kprintf("buf_size: %d, buf1: %lu, buf2: %lu\n", buf_size, buf1, buf2);
    
    while (cur) {
        buf[cur - 1] = (u8)(data & 0xff);
        data >>= 8;
        cur--;
    }
    
    for (i = 0; i < buf_size; i++) {
        char printable = buf[i];
        if (trans) {
            printable = process_key(buf[i]);
        }
        
        print_buf[print_count] = printable;
        if (printable != '\0') {
            print_count++;
        }
    }
    print_buf[print_count] = '\0';
    
    if (print_count) {
        stdin_write(get_activated_console_id(), print_buf, print_count);
    }
    
//     char printable = process_key(scan_code);
//     if (printable != '\0') {
//         kprintf("Got a keyboard interrupt, scan code: %u, printable: %c\n", scan_code, printable);
//         stdin_write(get_activated_console_id(), &printable, 1);
//     } else {
//         kprintf("Got a keyboard interrupt, scan code: %u, non-printable\n", scan_code);
//     }
    
    kapi_thread_exit(NULL);
}

void init_keyboard()
{
    // Register keyboard handler
    kapi_interrupt_reg(1, keyboard_interrupt_handler);
    kprintf("Keyboard handler registered\n");
}
